/**  
* @author renbangjie renbangjie@126.com  
* @date 2014-12-16 下午3:24:15
* @version V1.0  
* <b>Copyright (c)</b> 2014医信金融信息服务（上海）有限公司-版权所有<br/>
*/
package com.yjd.comm.util;

import java.util.Calendar;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;

import com.yjd.comm.enums.GenderEnum;

/**
 * @author renbangjie renbangjie@126.com
 * @date 2014-12-16 下午3:24:15
 */
public class CardNoUtils {
	
   /**
	 * 身份证号码验证 1、号码的结构 公民身份号码是特征组合码，由十七位数字本体码和一位校验码组成。排列顺序从左至右依次为：六位数字地址码，
	 * 八位数字出生日期码，三位数字顺序码和一位数字校验码。 2、地址码(前六位数）
	 * 表示编码对象常住户口所在县(市、旗、区)的行政区划代码，按GB/T2260的规定执行。 3、出生日期码（第七位至十四位）
	 * 表示编码对象出生的年、月、日，按GB/T7408的规定执行，年、月、日代码之间不用分隔符。 4、顺序码（第十五位至十七位）
	 * 表示在同一地址码所标识的区域范围内，对同年、同月、同日出生的人编定的顺序号， 顺序码的奇数分配给男性，偶数分配给女性。 5、校验码（第十八位数）
	 * （1）十七位数字本体码加权求和公式 S = Sum(Ai * Wi), i = 0, ... , 16 ，先对前17位数字的权求和
	 * Ai:表示第i位置上的身份证号码数字值 Wi:表示第i位置上的加权因子 Wi: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4
	 * 2 （2）计算模 Y = mod(S, 11) （3）通过模得到对应的校验码 Y: 0 1 2 3 4 5 6 7 8 9 10 校验码: 1 0
	 * X 9 8 7 6 5 4 3 2
	*/
	
	private static final int[] wi = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };
	
	/**
	 * 校验位
	 */
	private static final String[] valCodeArr = { "1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2" };
	
	public static boolean valid(String certNo){
		if(isEmpty(certNo)){
			return false;
		}
		
		//去特殊字符
		certNo = trim(certNo);
		
		//校验长度
		if(!length(certNo)){
			return false;
		}
		
		//数字位校验
		if(!isNumber(certNo)){
			return false;
		}
		
		if(!birthday(certNo)){
			return false;
		}
		
		String c = get15Or17(certNo);
		
		int temp = 0;
		
		for (int i = 0; i < 17; i++) {
			temp += Integer.parseInt(String.valueOf(c.charAt(i))) * wi[i];
		}
		int mod = temp % 11;
		
		String cardNo = c + valCodeArr[mod];
		
		if(cardNo.length() == 18){
			if(certNo.equalsIgnoreCase(cardNo)){
				return true;
			}
			return false;
		}
		
		return false;
		
	}
	
	/**
	 * 校验生日
	* @param certNo
	* @return boolean
	 */
	public static boolean birthday(String certNo){
		
		String birthday = getBirthday(certNo);
		
		if(StringUtils.isBlank(birthday)){
			return false;
		}
		
//		Matcher m = Pattern.compile("^((19[0-9]{2})|(20[0-1]{1}[0-4]{1}))-\\d{1,2}-\\d{1,2}$").matcher(birthday);
//		
//		if(!m.matches()){
//			return false;
//		}
		return true;
	}
	
	
	/**
	 * 获取生日
	* @param certNo
	* @return String
	 */
	public static String getBirthday(String certNo){
		
		certNo = get15Or17(certNo);
		if(StringUtils.isBlank(certNo)){
			return null;
		}
		
		String year = certNo.substring(6, 10);
		String month = certNo.substring(10, 12);
		String day = certNo.substring(12, 14);
		
		int y = Calendar.getInstance().get(Calendar.YEAR);
		
		if(!NumberUtils.isNumber(year)){
			return null;
		}
		
		int temp = y - Integer.parseInt(year);
		
		if(temp > 150 || temp < 1){
			return null;
		}
		
		if(!NumberUtils.isNumber(month)){
			return null;
		}
		
		temp = Integer.parseInt(month);
		
		if(temp > 12 || temp < 1){
			return null;
		}
		
		if(!NumberUtils.isNumber(day)){
			return null;
		}
		
		temp = Integer.parseInt(day);
		
		if(temp > 31 || temp <1){
			return null;
		}
		
		return year + "-" + month + "-" + day;
	}
	
	
	/**
	 * 获取性别
	 * @param sertNo
	 * @return GenderEnum
	 */
	public static GenderEnum getGender(String sertNo) {
		if (StringUtils.isBlank(sertNo)) {
			return GenderEnum.none;
		}
		int length = sertNo.length();
		String c = Character.valueOf(sertNo.charAt(length - 2)).toString();
		// 身份证倒数第二位偶数为女，奇数为男
		int sex = Integer.valueOf(c);
		if (sex % 2 == 0) {
			return GenderEnum.female;
		}
		return GenderEnum.male;
	}
	
	
	/**
	 * 校验年龄
	* @param certNo
	* @return int
	 * @throws Exception 
	 */
	public static int getAge(String certNo) throws Exception{
		
		String birthday = getBirthday(certNo);
		
		Calendar c = Calendar.getInstance();
		
		int year = c.get(Calendar.YEAR);
		
		String[] birthdays =  birthday.split("-");
		
		if(birthdays.length < 3){
			throw new Exception("身份证格式不正确");
		}
		
		String y = birthdays[0];
		
		if(!NumberUtils.isNumber(y)){
			throw new Exception("身份证生日年份不正确");
		}
		
		return year - Integer.parseInt(y);
		
	}
	
	
	public static boolean isEmpty(String certNo){
		if(StringUtils.isBlank(certNo)){
			return true;
		}
		return false;
	}
	
	/**
	 * 校验是否是纯数字
	* @param certNo
	* @return boolean
	 */
	public static boolean isNumber(String certNo){
		certNo = get15Or17(certNo);
		if(isEmpty(certNo)){
			return false;
		}
		if(NumberUtils.isNumber(certNo)){
			return true;
		}
		return false;
	}
	
	/**
	 * 如果是18位证件号返回前17位，15位返回全部
	* @param certNo
	* @return
	* boolean
	 */
	public static String get15Or17(String certNo){
		if(!length(certNo)){
			return null;
		}
		int length = certNo.length();
		
		if(length == 18){
			return certNo.substring(0, 17);
		}else{
			return certNo.substring(0, 6) + "19" + certNo.substring(6, 15);
		}
	}
	
	
	/**
	 * 校验长度
	* @param certNo
	* @return boolean
	 */
	public static boolean length(String certNo){
		if(certNo.length() != 15 && certNo.length() != 18){
			return false;
		}
		return true;
	}
	
	/**
	 * 去特殊字符
	* @param str
	* @return
	* String
	 */
	public static String trim(String str){
		return str.replaceAll("[^0-9a-zA-Z]|\\s", "");
	}
	
	
}
